package com.nightpiter.controller.reflection;

import static com.nightpiter.controller.log.Logger.log;
import static com.nightpiter.controller.log.Logger.logException;

import java.io.IOException;
import java.io.InputStream;
import java.util.Stack;

import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import com.nightpiter.controller.log.Logger;

/**
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public class PackReader {

	public static final String ROOT = "manifest";

	public static final String CONTENT = "content";

	public static final String NAME = "name";

	public static final String DESCRIPTION = "description";

	public static final String VERSION = "version";

	public static final String AUTHORS = "authors";

	public static final String AUTHOR = "author";

	public static final String NOTE = "note";

	public static final String MODULES = "modules";

	public static final String MODULE = "module";

	public static final String PACK = "pack";

	public static final String PREFIX = "prefix";

	public static final String CLASSES = "classes";

	public static final String CLASS = "class";

	public static final String ID_ATTR = "id";

	public static final String MAIN_ATTR = "main";

	public static final String LAST_MODIFIED_ATTR = "last-modified";

	/**
	 * 
	 * @param inputStream
	 * @param packBuilder
	 *            target manifest to read in
	 * @throws IOException
	 */
	public static void read(InputStream inputStream, PackBuilder packBuilder)
			throws IOException {
		try {
			SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
			XMLReader xr = parser.getXMLReader();
			EventsHandler handler = new EventsHandler(packBuilder);

			xr.setContentHandler(handler);
			xr.setErrorHandler(handler);

			InputSource inputSource = new InputSource(inputStream);

			xr.parse(inputSource);

		} catch (FactoryConfigurationError fce) {
			logException(fce);
			log("Parsing failed");
		} catch (ParserConfigurationException pce) {
			logException(pce);
			log("Parsing failed");
		} catch (SAXParseException spe) {
			logException(spe);
			log("Parsing failed");
		} catch (SAXException saxe) {
			logException(saxe);
			log("Parsing failed");
		}

	}

	/**
	 * This class handles events occurring in the SAX processing action
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	public static class EventsHandler extends DefaultHandler {

		private String version = null;

		private Stack<String> stack;

		private PackBuilder packBuilder;

		public EventsHandler(PackBuilder packBuilder) {
			super();
			this.packBuilder = packBuilder;
			this.stack = new Stack<String>();
		}

		public void startDocument() {
		}

		public void endDocument() {
		}

		public void startElement(String uri, String name, String qName,
				Attributes attrs) {

			stack.push(name);
			String path = getPath();

			if (path.equals(ROOT + MODULES + MODULE + CLASSES + CLASS)) {
				int id = Integer.parseInt(attrs.getValue(ID_ATTR));
				packBuilder.startClass(id);

			} else if (path.equals(ROOT + MODULES + MODULE + CLASSES)) {
				int main = Integer.parseInt(attrs.getValue(MAIN_ATTR));
				packBuilder.startClasses(main);

			} else if (path.equals(ROOT + MODULES + MODULE)) {
				packBuilder.startModule();

			} else if (path.equals(ROOT)) {
				this.version = attrs.getValue(VERSION);
				Logger.log("xml file version retrieved: " + this.version);
			}
		}

		public void endElement(String uri, String name, String qName) {

			String path = getPath();
			if (path.equals(ROOT + MODULES + MODULE + CLASSES + CLASS)) {
				packBuilder.endClass();

			} else if (path.equals(ROOT + MODULES + MODULE + CLASSES)) {
				packBuilder.endClasses();

			} else if (path.equals(ROOT + MODULES + MODULE)) {
				packBuilder.endModule();

			}

			stack.pop();
		}

		public void characters(char ch[], int start, int length) {
			String path = getPath();

			if (path.equals(ROOT + MODULES + MODULE + CLASSES + CLASS)) {
				packBuilder.setClassName(new String(ch, start, length));

			} else if (path.equals(ROOT + MODULES + MODULE + NAME)) {
				packBuilder.setModuleName(new String(ch, start, length));

			} else if (path.equals(ROOT + MODULES + MODULE + PREFIX)) {
				packBuilder.setModulePrefix(new String(ch, start, length));

			} else if (path.equals(ROOT + DESCRIPTION + PACK + NAME)) {
				packBuilder.setPackName(new String(ch, start, length));

			} else if (path.equals(ROOT + DESCRIPTION + NOTE)) {
				packBuilder.setNote(new String(ch, start, length));

			} else if (path
					.equals(ROOT + DESCRIPTION + AUTHORS + AUTHOR + NAME)) {
				packBuilder.setPackAuthorName(new String(ch, start, length));

			} else if (path.equals(ROOT + MODULES + MODULE + AUTHORS + AUTHOR
					+ NAME)) {
				packBuilder.setModuleAuthorName(new String(ch, start, length));

			}
		}

		private String getPath() {
			String path = "";
			for (int i = 0; i < stack.size(); i++) {
				path += stack.get(i);
			}
			return path;
		}
	}

}
